﻿
// 2020/1/16: 首个记录
// 2020/3/10: 增加红灯/绿灯闪烁状态
// 2020/5/15: 移除编译warning
// 2020/7/20: 更新样本标题
// 2020/11/2: 增加停车位是否被占用字段
// 2020/11/12: 支持Spadas7

#ifndef TRAFFIC_SAMPLE_V2_H
#define TRAFFIC_SAMPLE_V2_H

#include "spadas.h"

namespace traffic_sample_v2
{
	using namespace spadas;

#if SPADAS_VERSION_MAJOR < 7
	typedef OptionalDouble GeneralElement;
#endif

	// Elements
	enum TrafficElementType
	{
		TET_Invalid = 0,
		TET_SpeedLimit = 1, // 限速标志
		TET_StopSign = 2, // 停止标志
		TET_TrafficLight = 3, // 交通信号灯
		TET_StopLine = 4, // 停止线
		TET_SpeedBump = 5, // 减速带
		TET_ArrowMark = 6, // 路面箭头
		TET_ProhibitionZone = 7, // 禁停区
		TET_ParkingSlot = 8, // 停车位
		TET_ZebraCrossing = 9, // 斑马线
	};

	struct OptBool
	{
		Bool valid;
		Bool value;
	};

	struct OptDouble
	{
		Bool valid;
		Double value;
	};

	struct ArrowCombination
	{
		Bool valid; // 是否有效，若不分方向(如实心圆交通灯信号)则为FALSE
		Bool goStraight; // 直行
		Bool turnLeft; // 左转
		Bool turnRight; // 右转
		Bool uTurn; // 调头
	};

	struct SpeedLimitData
	{
		UInt limitedSpeed; // 限速 KPH
		OptDouble posX; // 纵向位置 m
		OptDouble posY; // 横向位置 m
		OptDouble posZ; // 垂直位置 m
	};

	struct StopSignData
	{
		OptDouble posX; // 纵向位置 m
		OptDouble posY; // 横向位置 m
		OptDouble posZ; // 垂直位置 m
	};

	enum TrafficLightState
	{
		TLS_Invalid = 0,
		TLS_Stop = 1, // 红灯
		TLS_Continue = 2, // 黄灯
		TLS_Caution = 3, // 黄灯闪烁
		TLS_Allow = 4, // 绿灯
		TLS_AllowFlash = 5, // 绿灯闪烁
		TLS_StopFlash = 6, // 红灯闪烁
	};

	struct TrafficLightData
	{
		TrafficLightState state; // 信号灯状态
		ArrowCombination arrows; // 信号灯的箭头组合
		OptDouble posX; // 纵向位置 m
		OptDouble posY; // 横向位置 m
		OptDouble posZ; // 垂直位置 m
	};

	struct StopLineData
	{
		Double distance; // 停止线与x轴相交位置 m
		OptDouble heading; // 停止线法线朝向，单位°，逆时针为正方向
	};

	struct SpeedBumpData
	{
		Double distance; // 减速带与x轴相交位置 m
		OptDouble heading; // 减速带法线朝向，单位°，逆时针为正方向
	};

	struct ArrowMarkData
	{
		ArrowCombination arrows; // 路面标志的箭头组合
		OptDouble posX; // 纵向位置 m
		OptDouble posY; // 横向位置 m
	};

	struct ProhibitionZoneData
	{
		Double centerX; // 禁停区中央的纵向位置 m
		Double centerY; // 禁停区中央的横向位置 m
		Double width; // 禁停区的宽度 m
		Double length; // 禁停区的长度 m
		OptDouble heading; // 禁停区的朝向(沿长度方向)，单位°，逆时针为正方向
	};

	enum ParkingSlotOccupancy
	{
		PSO_Unknown = 0, // 未知
		PSO_NotOccupied = 1, // 未被占用
		PSO_Occupied = 2, // 被占用
	};

	struct ParkingSlotData
	{
		Double centerX; // 停车位中央的纵向位置 m
		Double centerY; // 停车位中央的横向位置 m
		Double width; // 停车位的宽度 m
		Double length; // 停车位的长度 m
		OptDouble heading; // 停车位的朝向(沿长度方向)，单位°，逆时针为正方向
		ParkingSlotOccupancy occupancy; // 停车位是否被占用
	};

	struct ZebraCrossingData
	{
		Double nearDistance; // 斑马线近端与x轴相交位置 m
		OptDouble farDistance; // 斑马线远端与x轴相交位置 m
		OptDouble heading; // 斑马线的朝向，单位°，逆时针为正方向
	};

	// Sample
	union TrafficElementData
	{
		SpeedLimitData speedLimit;
		StopSignData stopSign;
		TrafficLightData trafficLight;
		StopLineData stopLine;
		SpeedBumpData speedBump;
		ArrowMarkData arrowMark;
		ProhibitionZoneData prohibitionZone;
		ParkingSlotData parkingSlot;
		ZebraCrossingData zebraCrossing;
	};

	struct TrafficElement
	{
		TrafficElementType type; // 交通元素类别
		Int id; // 交通元素ID，0表示无效
		OptionalInt rawID; // 交通元素的原始ID
		TrafficElementData data; // 交通元素数据
	};

	struct TrafficSample
	{
		GlobalTimestamp timeStamp; // 时间戳
		Array<TrafficElement> elements; // 交通元素列表

		TrafficSample()
		{}

		static String getProtocolName(UInt channel)
		{
			if (channel == UINF) return "traffic-sample-v2";
			else return (String)"traffic-sample-v2@" + channel;
		}

		static String getTitle()
		{
			return "Traffic element count,First element's data's offset,First element's data's size,First element's type,First element's ID,First element's raw ID,Reserved,Second element's type / First data value,etc";
		}

		static Bool supportInterpolation()
		{
			return FALSE;
		}

		static OptionalDouble convert(OptBool val)
		{
			return val.valid ? (val.value ? 1 : 0) : OptionalDouble();
		}
		static OptionalDouble convert(OptDouble val)
		{
			return val.valid ? val.value : OptionalDouble();
		}

		GeneralSample toGeneralSample()
		{
			GeneralSample sample;
			sample.timeStamp = timeStamp;

			Array<UInt> offsets(elements.size());
			Array<UInt> lengthes(elements.size());
			UInt totalDataSize = 0;
			for (UInt i = 0; i < elements.size(); i++)
			{
				UInt len = 0;
				switch (elements[i].type)
				{
				case TET_SpeedLimit:
					len = 6;
					break;
				case TET_StopSign:
					len = 5;
					break;
				case TET_TrafficLight:
					len = 10;
					break;
				case TET_StopLine:
					len = 4;
					break;
				case TET_SpeedBump:
					len = 4;
					break;
				case TET_ArrowMark:
					len = 8;
					break;
				case TET_ProhibitionZone:
					len = 7;
					break;
				case TET_ParkingSlot:
					len = 7;
					break;
				case TET_ZebraCrossing:
					len = 5;
					break;
				default:
					break;
				}
				offsets[i] = totalDataSize;
				lengthes[i] = len;
				totalDataSize += len;
			}

			sample.values = Array<GeneralElement>(1 + elements.size() * 6 + totalDataSize);
			UInt base = 1 + elements.size() * 6;

			sample.values[0] = (Double)elements.size();
			for (UInt i = 0; i < elements.size(); i++)
			{
				sample.values[1 + 6 * i] = offsets[i];
				sample.values[2 + 6 * i] = lengthes[i];
				sample.values[3 + 6 * i] = (UInt)elements[i].type;
				sample.values[4 + 6 * i] = elements[i].id;
				sample.values[5 + 6 * i] = elements[i].rawID.valid ? (Double)elements[i].rawID.value : OptionalDouble();

				UInt eBase = base + offsets[i];
				switch (elements[i].type)
				{
				case TET_SpeedLimit:
					sample.values[eBase] = elements[i].data.speedLimit.limitedSpeed;
					sample.values[eBase + 1] = convert(elements[i].data.speedLimit.posX);
					sample.values[eBase + 2] = convert(elements[i].data.speedLimit.posY);
					sample.values[eBase + 3] = convert(elements[i].data.speedLimit.posZ);
					break;
				case TET_StopSign:
					sample.values[eBase] = convert(elements[i].data.stopSign.posX);
					sample.values[eBase + 1] = convert(elements[i].data.stopSign.posY);
					sample.values[eBase + 2] = convert(elements[i].data.stopSign.posZ);
					break;
				case TET_TrafficLight:
					sample.values[eBase] = (UInt)elements[i].data.trafficLight.state;
					sample.values[eBase + 1] = convert(elements[i].data.trafficLight.posX);
					sample.values[eBase + 2] = convert(elements[i].data.trafficLight.posY);
					sample.values[eBase + 3] = convert(elements[i].data.trafficLight.posZ);
					if (elements[i].data.trafficLight.arrows.valid)
					{
						sample.values[eBase + 4] = elements[i].data.trafficLight.arrows.goStraight ? 1 : 0;
						sample.values[eBase + 5] = elements[i].data.trafficLight.arrows.turnLeft ? 1 : 0;
						sample.values[eBase + 6] = elements[i].data.trafficLight.arrows.turnRight ? 1 : 0;
						sample.values[eBase + 7] = elements[i].data.trafficLight.arrows.uTurn ? 1 : 0;
					}
					break;
				case TET_StopLine:
					sample.values[eBase] = elements[i].data.stopLine.distance;
					sample.values[eBase + 1] = convert(elements[i].data.stopLine.heading);
					break;
				case TET_SpeedBump:
					sample.values[eBase] = elements[i].data.speedBump.distance;
					sample.values[eBase + 1] = convert(elements[i].data.speedBump.heading);
					break;
				case TET_ArrowMark:
					sample.values[eBase] = elements[i].data.arrowMark.arrows.goStraight ? 1 : 0;
					sample.values[eBase + 1] = elements[i].data.arrowMark.arrows.turnLeft ? 1 : 0;
					sample.values[eBase + 2] = elements[i].data.arrowMark.arrows.turnRight ? 1 : 0;
					sample.values[eBase + 3] = elements[i].data.arrowMark.arrows.uTurn ? 1 : 0;
					sample.values[eBase + 4] = convert(elements[i].data.arrowMark.posX);
					sample.values[eBase + 5] = convert(elements[i].data.arrowMark.posY);
					break;
				case TET_ProhibitionZone:
					sample.values[eBase] = elements[i].data.prohibitionZone.centerX;
					sample.values[eBase + 1] = elements[i].data.prohibitionZone.centerY;
					sample.values[eBase + 2] = elements[i].data.prohibitionZone.width;
					sample.values[eBase + 3] = elements[i].data.prohibitionZone.length;
					sample.values[eBase + 4] = convert(elements[i].data.prohibitionZone.heading);
					break;
				case TET_ParkingSlot:
					sample.values[eBase] = elements[i].data.parkingSlot.centerX;
					sample.values[eBase + 1] = elements[i].data.parkingSlot.centerY;
					sample.values[eBase + 2] = elements[i].data.parkingSlot.width;
					sample.values[eBase + 3] = elements[i].data.parkingSlot.length;
					sample.values[eBase + 4] = convert(elements[i].data.parkingSlot.heading);
					sample.values[eBase + 5] = (UInt)elements[i].data.parkingSlot.occupancy;
					break;
				case TET_ZebraCrossing:
					sample.values[eBase] = elements[i].data.zebraCrossing.nearDistance;
					sample.values[eBase + 1] = convert(elements[i].data.zebraCrossing.farDistance);
					sample.values[eBase + 2] = convert(elements[i].data.zebraCrossing.heading);
					break;
				default:
					break;
				}
			}

			sample.significantCount = 1 + elements.size() * 6 + totalDataSize;
			return sample;
		}

		Bool fromGeneralSample(GeneralSample sample)
		{
			// 未实现
			return FALSE;
		}

		Bool fromGeneralSample(String protocol, GeneralSample sample)
		{
			// 未实现
			return FALSE;
		}

		static TrafficSample interpolate(TrafficSample& s1, Double w1, TrafficSample& s2, Double w2, GlobalTimestamp time)
		{
			return TrafficSample();
		}
	};
}

#endif
